/*
 * Copyright (C) 2008-2011 CBDP Consortium
 * 
 * All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.
 * 
 * This software consists of contributions made by many individuals, 
 * listed below:
 *
 * Author: 	Xabier Laiseca <xabier.laiseca@deusto.es>
 * 			David Ausín <david.ausin@deusto.es>
 */
package cbdp.server.contextreasoner.internal.containers;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import com.hp.hpl.jena.assembler.Assembler;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.ontology.OntModelSpec;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.shared.Lock;
import com.hp.hpl.jena.util.FileManager;
import com.hp.hpl.jena.vocabulary.RDF;


public class MemoryOntologyContainer extends BaseOntologyContainer
{
	private final static String ONTOLOGY_STORAGE_DIRECTORY 	= "data/ontology/";
	private final static String ONTOLOGY_STORAGE_FILE 		= ONTOLOGY_STORAGE_DIRECTORY + "cbdp-ontology.owl";
	
	private final static int PERIOD_BETWEEN_SAVING_THE_ONTOLOGY_TO_FILE = 5;
	private final static TimeUnit TIME_UNIT_OF_PERIOD_BETWEEN_SAVING_THE_ONTOLOGY_TO_FILE = TimeUnit.MINUTES;
	
	
	@Override
	public void load()
	{
		final Model specModel = FileManager.get().loadModel( getConfiguration().getStorageConfigurationFileLocation() );
		final Resource storageRoot = getStorageRootFromSpec(specModel);
		
		final Model model = Assembler.general.openModel(storageRoot);
		final OntModelSpec ontModelSpec = getOntModelSpec();
		final OntModel ontModel = ModelFactory.createOntologyModel(ontModelSpec, model);
		
		if(existsFile(ONTOLOGY_STORAGE_FILE))
		{
			ontModel.read("file:" + ONTOLOGY_STORAGE_FILE);
		}
		
		setOntModel(ontModel);
		
		startOntologySaverExecutor();
	}
	
	private ScheduledThreadPoolExecutor executor;
	private ScheduledFuture<?> sf;
	
	private void startOntologySaverExecutor()
	{
		executor = new ScheduledThreadPoolExecutor(1);
		sf = executor.scheduleAtFixedRate(new SaveOntologyTask(), PERIOD_BETWEEN_SAVING_THE_ONTOLOGY_TO_FILE, 
				PERIOD_BETWEEN_SAVING_THE_ONTOLOGY_TO_FILE, TIME_UNIT_OF_PERIOD_BETWEEN_SAVING_THE_ONTOLOGY_TO_FILE);
	}
	
	private void stopOntologySaverExecutor()
	{
		sf.cancel(true);
		executor.shutdown();
	}
	
	private class SaveOntologyTask implements Runnable
	{
		@Override
		public void run()
		{
			saveOntology();
		}	
	}
	
	private boolean existsFile(final String name)
	{
		final File file = new File(name);
		return file.exists();
	}
	
	private Resource getStorageRootFromSpec(final Model specModel)
	{
		final Resource memoryModelRes = specModel.getResource("http://jena.hpl.hp.com/2005/11/Assembler#MemoryModel");
		return specModel.listResourcesWithProperty(RDF.type, memoryModelRes).next();
	}

	@Override
	public void close()
	{
		stopOntologySaverExecutor();
		
		saveOntology();
		
		getOntModel().close();
	}

	private void saveOntology()
	{
		final File ontologyStorageDirectory = new File(ONTOLOGY_STORAGE_DIRECTORY);
		
		if (!ontologyStorageDirectory.exists())
		{
			ontologyStorageDirectory.mkdirs();
		}
		
		if(ontologyStorageDirectory.exists())
		{
			try
			{
				final FileOutputStream ontologyStorageFile = new FileOutputStream(ONTOLOGY_STORAGE_FILE);
				getOntModel().enterCriticalSection(Lock.READ);
				getOntModel().write(ontologyStorageFile);
				ontologyStorageFile.close();
			}
			catch (FileNotFoundException e)
			{
				System.out.println("The ontology can not be stored.");
			}
			catch (IOException e)
			{
				System.out.println("The file can not be closed.");
			}
			finally
			{
				getOntModel().leaveCriticalSection();
			}
		}
	}

}
